package hust.hiepdv.javaproject.networking;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JOptionPane;

import hust.hiepdv.javaproject.constant.Constant;
import hust.hiepdv.javaproject.model.Ball;
import hust.hiepdv.javaproject.model.Pad;
import hust.hiepdv.javaproject.view.ClientView;
import hust.hiepdv.javaproject.view.ClientView;
import hust.hiepdv.javaproject.view.DrawPanel;
import hust.hiepdv.javaproject.view.StartGameFrame;

public class TCPClient implements Runnable, Constant{

	private Socket clientSocket = null;
	private PrintStream outToServer = null;
	private BufferedReader inFromServer = null;
	private int portNumber = SERVER_TCP_PORT;
	private String host = SERVER_IP; 
	//private UDPClient UDPClient;
	private boolean isStopped = false;
	private boolean isPlaying = true;
	
	public Ball ball = new Ball();
	public Pad[] pads = new Pad[4];
	
	public static int id;
	
	DrawPanel drawPanel;
	StartGameFrame startFrame;
	ClientView clientView;
	
	public TCPClient(ClientView clientView) throws Exception {
		this.clientView = clientView;
		this.ball = ClientView.ball;
		this.pads[0] = ClientView.pad1;
		this.pads[1] = ClientView.pad2;
		this.pads[2] = ClientView.pad3;
		this.pads[3] = ClientView.pad4;
	}
	
	@Override
	public void run() {
		try {
			clientSocket = new Socket(clientView.tbServerIP.getText().trim(), portNumber);
			inFromServer = new BufferedReader(new InputStreamReader(
					clientSocket.getInputStream()));
			outToServer = new PrintStream(clientSocket.getOutputStream());
			
			/*
			 * Make a message send to server inform that it wants to join the game
			 */
			//String msg = JOIN_GAME_MSG + "|" + UDPClient.getPort();			
			//outToServer.println(msg);
			String msg = JOIN_GAME_MSG+"";
			outToServer.println(msg);
			
			while(!isStopped()){
				String line = inFromServer.readLine();
				String[] msgItems = line.split(SPLITER);
				if(msgItems[0].equals(""+STOP_GAME)){
					JOptionPane.showMessageDialog(clientView.getFrame(), "Game Stop!");
					setIsPlaying(false);					
					drawPanel.stop();
					clientView.reset();
					clientView.btnJoinGame.setEnabled(true);
					break;
				} else if(msgItems[0].equals(""+BUSY_MSG)){
					JOptionPane.showMessageDialog(clientView.getFrame(), "Server is busy now! Game is playing");
					break;
				}
				else {
					handleMsg(msgItems);
				}
			}
			
			inFromServer.close();
			outToServer.close();
			clientSocket.close();
			setIsPlaying(false);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public synchronized boolean isPlaying() {
		return this.isPlaying;
	}

	public synchronized void setIsPlaying(boolean b) {
		this.isPlaying = b;
	}
	
	public void startSend() {
		setIsPlaying(true);
		SendThread sendThread = new SendThread();
		Thread thread = new Thread(sendThread);
		thread.start();
	}

	class SendThread extends Thread {
		@Override
		public void run() {
			while (isPlaying()) {
				String s = packServerData();
				outToServer.println(s);
				try {
					Thread.sleep(TIME_SLEEP);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}				
			}
		}
	}
	
	public String packServerData() {
		StringBuilder sb = new StringBuilder();
		sb.append(DATA_MSG);
		sb.append("#");
		sb.append(pads[id].getX());
		sb.append("#");
		sb.append(pads[id].getY());
		return sb.toString();
	}
	
	public void handleMsg(String[] msgItems) throws Exception{
		int commandCode = Integer.parseInt(msgItems[0]);
		switch (commandCode) {
		case JOIN_SUCCESS_MSG:			
			System.out.println("JoinSuccess");
			this.id = Integer.parseInt(msgItems[1]);	
			drawPanel = new DrawPanel();
			drawPanel.setBounds(5, 8, 600, 600);			
			//this.clientView.getFrame().addKeyListener(drawPanel.keyAdapter);
			this.clientView.getFrame().getContentPane().add(drawPanel);
			this.clientView.btnJoinGame.setEnabled(false);
			arrangeTable(id);
			Thread thread = new Thread(drawPanel);
			thread.start();
			//UDPClient = new UDPClient(msgItems[1]);
			break;
		case START_GAME:
			this.startSend();
			break;
		case DATA_MSG:
			//System.out.println("Received Data");
			handleData(id, msgItems);
			break;
		default:
			break;
		}
	}
	
	public void handleData(int id, String[] msgItems){
		clientView.updateScore(Integer.parseInt(msgItems[11]), Integer.parseInt(msgItems[12]), 
				Integer.parseInt(msgItems[13]), Integer.parseInt(msgItems[14]));
		switch (id) {
		case 0:
			this.ball.setX(Float.parseFloat(msgItems[1]));
			this.ball.setY(Float.parseFloat(msgItems[2]));
			int index = 3;
			for (int i = 0; i < 4; i++) {
				if (i != 0) {
					this.pads[i].setX(Integer.parseInt(msgItems[index]));
					index++;
					this.pads[i].setY(Integer.parseInt(msgItems[index]));
					index++;
				} else {
					index +=2;
				}
			}
			break;
		case 1:
			/*
			 * Ball: x = y, y = TABLE_SIZE - x - BALL_SIZE
			 */
			this.ball.setX(Float.parseFloat(msgItems[2]));
			this.ball.setY(TABLE_SIZE - Float.parseFloat(msgItems[1]) - BALL_SIZE);
			this.pads[0].setY(TABLE_SIZE -Integer.parseInt(msgItems[3]) - PAD_LENGTH); // y = TABLE_SIZE - x - PAD_LENGT
			this.pads[2].setY(TABLE_SIZE - Integer.parseInt(msgItems[7]) - PAD_LENGTH); // y = TABLE_SIZE - x - PAD_LENGT
			this.pads[3].setX(Integer.parseInt(msgItems[10])); //x = y
			break;
		case 2:
			this.ball.setX(TABLE_SIZE - Float.parseFloat(msgItems[1]) - BALL_SIZE);
			this.ball.setY(TABLE_SIZE - Float.parseFloat(msgItems[2]) - BALL_SIZE);
			this.pads[0].setX(TABLE_SIZE -Integer.parseInt(msgItems[3]) - PAD_LENGTH); // x = TABLE_SIZE - x - PAD_LENGT
			this.pads[1].setY(TABLE_SIZE - Integer.parseInt(msgItems[6]) - PAD_LENGTH); // y = TABLE_SIZE - y - PAD_LENGT
			this.pads[3].setY(TABLE_SIZE - Integer.parseInt(msgItems[10]) - PAD_LENGTH);
			break;
		case 3:
			this.ball.setX(TABLE_SIZE - Float.parseFloat(msgItems[2]) - BALL_SIZE);
			this.ball.setY(Float.parseFloat(msgItems[1]));
			this.pads[0].setY(Integer.parseInt(msgItems[3])); // y = x
			this.pads[1].setX(TABLE_SIZE - Integer.parseInt(msgItems[6]) - PAD_LENGTH); // x = TABLE_SIZE - y - PAD_LENGT
			this.pads[2].setY(Integer.parseInt(msgItems[7]));// y = x
			break;
		default:
			break;
		}
	}
	
	public void arrangeTable(int id){
		switch (id) {
		case 0:
			//do nothing
			break;
		case 1:
			ClientView.pad1.setData(PAD4_X, TABLE_SIZE - PAD1_INIT_X - PAD_LENGTH, Pad.STYLE_1, Pad.VERTICAL);
			ClientView.pad2.setData(PAD2_INIT_Y, PAD1_Y, Pad.STYLE_2, Pad.HORIZONTAL);
			ClientView.pad3.setData(PAD2_X, TABLE_SIZE - PAD3_INIT_X - PAD_LENGTH, Pad.STYLE_3, Pad.VERTICAL);
			ClientView.pad4.setData(PAD4_INIT_Y, PAD3_Y, Pad.STYLE_4, Pad.HORIZONTAL);
			break;
		case 2:
			ClientView.pad1.setData(TABLE_SIZE - PAD1_INIT_X - PAD_LENGTH, PAD3_Y, Pad.STYLE_1, Pad.HORIZONTAL);
			ClientView.pad2.setData(PAD4_X, TABLE_SIZE - PAD2_INIT_Y - PAD_LENGTH, Pad.STYLE_2, Pad.VERTICAL);
			ClientView.pad3.setData(TABLE_SIZE - PAD3_INIT_X - PAD_LENGTH, PAD1_Y, Pad.STYLE_3, Pad.HORIZONTAL);
			ClientView.pad4.setData(PAD2_X, TABLE_SIZE - PAD4_INIT_Y - PAD_LENGTH, Pad.STYLE_4, Pad.VERTICAL);
			break;
		case 3:
			ClientView.pad1.setData(PAD2_X, PAD1_INIT_X, Pad.STYLE_1, Pad.VERTICAL);
			ClientView.pad2.setData(TABLE_SIZE - PAD2_INIT_Y - PAD_LENGTH, PAD3_Y, Pad.STYLE_2, Pad.HORIZONTAL);
			ClientView.pad3.setData(PAD4_X, PAD3_INIT_X, Pad.STYLE_3, Pad.VERTICAL);
			ClientView.pad4.setData(TABLE_SIZE - PAD4_INIT_Y - PAD_LENGTH, PAD1_Y, Pad.STYLE_4, Pad.HORIZONTAL);
			break;
		default:
			break;
		}
	}
	
	public synchronized boolean isStopped(){
		return this.isStopped;
	}
	
	public synchronized void stop(){
		this.isStopped = true;
		try{
			this.outToServer.println(CLIENT_STOP_PLAY_MSG);
			this.clientSocket.close();
		} catch (IOException e){
			throw new RuntimeException("Error closing server", e);
		}
	}

	public Socket getClientSocket() {
		return clientSocket;
	}

	public void setClientSocket(Socket clientSocket) {
		this.clientSocket = clientSocket;
	}

	public PrintStream getOutToServer() {
		return outToServer;
	}

	public void setOutToServer(PrintStream outToServer) {
		this.outToServer = outToServer;
	}

	public BufferedReader getInFromServer() {
		return inFromServer;
	}

	public void setInFromServer(BufferedReader inFromServer) {
		this.inFromServer = inFromServer;
	}

}
